; docformat = 'rst'
;
; NAME:
;       CDF_VarSelect__DEFINE
;
;*****************************************************************************************
;   Copyright (c) 2013, Matthew Argall                                                   ;
;   All rights reserved.                                                                 ;
;                                                                                        ;
;   Redistribution and use in source and binary forms, with or without modification,     ;
;   are permitted provided that the following conditions are met:                        ;
;                                                                                        ;
;       * Redistributions of source code must retain the above copyright notice,         ;
;         this list of conditions and the following disclaimer.                          ;
;       * Redistributions in binary form must reproduce the above copyright notice,      ;
;         this list of conditions and the following disclaimer in the documentation      ;
;         and/or other materials provided with the distribution.                         ;
;       * Neither the name of the <ORGANIZATION> nor the names of its contributors may   ;
;         be used to endorse or promote products derived from this software without      ;
;         specific prior written permission.                                             ;
;                                                                                        ;
;   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY  ;
;   EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES ;
;   OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT  ;
;   SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,       ;
;   INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED ;
;   TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR   ;
;   BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN     ;
;   CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN   ;
;   ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH  ;
;   DAMAGE.                                                                              ;
;*****************************************************************************************
;
; PURPOSE:
;+
;       A class for selecting CDF files and variables from within those files.
;
; :Author:
;       Matthew Argall::
;		University of New Hampshire
;		Morse Hall, Room 113
;       8 College Rd.
;		Durham, NH, 03824
;       matthew.argall@wildcats.unh.edu
;
; :Categories:
;
;       CDF Utilities, File I/O, Data Reader
;
; :History:
;   Modification History::
;       08/08/2012  -   Written by Matthew Argall
;       09/15/2013  -   Depend 3 variables were not being placed in the widget tree. Fixed.
;                           Because this is a blocking widget, attempts to destroy the
;                           object always occur within the INIT method, causing an error.
;                           To subvert this, a CANCEL property was added and is checked
;                           within INIT after the widget has been realized and the
;                           XManager has returned. Banners and lists are updating properly.
;                           - MRA
;-
;*****************************************************************************************
;+
;   The purpose of this method is to create a node and affix it to the widget tree.
;   Nodes will have the following structure::
;
;       ROOT
;           Variable
;               Depend_1
;               Depend_2
;               Depend_3
;
; :Params:
;       VNAME:          in, required, type=string
;                       The variable name to be attached to the Tree root.
;       DEPEND_1        in, optional, type=string
;                       The name of the depend_1 variable associated with `VNAME`
;       DEPEND_2        in, optional, type=string
;                       The name of the depend_2 variable associated with `VNAME`
;       DEPEND_3        in, optional, type=string
;                       The name of the depend_3 variable associated with `VNAME`
;-
pro CDF_VarSelect::Create_Node, vname, depend_1, depend_2, depend_3
    compile_opt idl2
    
    ;catch errors
    catch, the_error
    if the_error ne 0 then begin
        catch, /cancel
        void = cgErrorMsg()
        return
    endif
    
    ;Keep track of the widget IDs and user name of each new branch/leaf, plus the widget
    ;ID of their parent. In addition, count how many new branches/leaves have been made.
    tree_ids = lonarr(4)
    parent_ids = lonarr(4)
    unames = strarr(4)
    count = 0

;---------------------------------------------------------------------
;Variable Leaf ///////////////////////////////////////////////////////
;---------------------------------------------------------------------
    
    ;If there is no DEPEND_1, then create a single leaf with the variable name
    if n_elements(depend_1) eq 0 then begin
        tree_ids[count] = widget_tree(self.rootID, VALUE=vname, UNAME=vname, $
                                      UVALUE={object: self, method: 'Tree_Events'})
        parent_ids[count] = self.rootID
        unames[count] = vname
        count += 1
        
;---------------------------------------------------------------------
;Variable Branch /////////////////////////////////////////////////////
;---------------------------------------------------------------------

    ;Otherwise, if there are dependent variables
    endif else begin
        ;Turn the variable name into a folder.
        tree_ids[count] = widget_tree(self.rootID, VALUE=vname, UNAME=vname, /FOLDER, $
                                      UVALUE={object: self, method: 'Tree_Events'})
        
        ;The variable's parent is the tree's root
        parent_ids[count] = self.rootID
        unames[count] = vname
        count += 1
        
    ;---------------------------------------------------------------------
    ;DEPEND_1 ////////////////////////////////////////////////////////////
    ;---------------------------------------------------------------------
        
        ;DEPEND_1
        ;   Create a leaf for the DEPEND_1 variable. Its parent is the variable folder
        ;   just created -- the widget ID of the folder is stored at index (count-1).
        tree_ids[count] = widget_tree(tree_ids[count-1], VALUE=depend_1, $
                                      UNAME='DEPEND_1: ' + depend_1, $
                                      UVALUE={object: self, method: 'Tree_Events'})
                                      
        ;Prefix the username by the DEPEND_#.
        parent_ids[count] = tree_ids[count-1]
        unames[count] = 'DEPEND_1: ' + depend_1
        count +=1
        
    ;---------------------------------------------------------------------
    ;DEPEND_2 ////////////////////////////////////////////////////////////
    ;---------------------------------------------------------------------
        
        ;Repeat if DEPEND_2 exists.
        if n_elements(depend_2) ne 0 then begin
            tree_ids[count] = widget_tree(tree_ids[count-2], VALUE=depend_2, $
                                          UNAME='DEPEND_2: ' + depend_2, $
                                          UVALUE={object: self, method: 'Tree_Events'})
            parent_ids[count] = tree_ids[count-2]
            unames[count] = 'DEPEND_2: ' + depend_2
            count +=1
        endif
        
    ;---------------------------------------------------------------------
    ;DEPEND_3 ////////////////////////////////////////////////////////////
    ;---------------------------------------------------------------------
        
        ;Repeat if DEPEND_3 exists.
        if n_elements(depend_3) ne 0 then begin
            tree_ids[count] = widget_tree(tree_ids[count-3], VALUE=depend_3, $
                                          UNAME='DEPEND_3: ' + depend_3, $
                                          UVALUE={object: self, method: 'Tree_Events'})
            parent_ids[count] = tree_ids[count-3]
            unames[count] = 'DEPEND_3: ' + depend_3
            count +=1
        endif
    endelse
        
;---------------------------------------------------------------------
;Update the Object Property //////////////////////////////////////////
;---------------------------------------------------------------------
    
    ;Store the parent ID and user name then increase the count
    *self.tree_ids = [*self.tree_ids, tree_ids[0:count-1]]
    *self.parent_ids = [*self.parent_ids, parent_ids[0:count-1]]
    *self.unames = [*self.unames, unames[0:count-1]]
end


;+
;   The purpose of this method is to manage how the widget tree is populated and to
;   change the widget label text to describe how many variables are shown/hidden.
;
; :Params:
;       EVENT:              in, required, type=structure
;                           An event structure returned by the windows manager.
;-
pro CDF_VarSelect::Create_Tree, event
    compile_opt idl2
    
    ;catch errors
    catch, the_error
    if the_error ne 0 then begin
        catch, /cancel
        void = cgErrorMsg()
        return
    endif
    
    ;Get the state variable and toggle the SHOW_ALL bit
    self.show_all = abs(self.show_all - 1)

;---------------------------------------------------------------------
;Destroy the Previous Tree Structure /////////////////////////////////
;---------------------------------------------------------------------    
    ;Find which widgets have the root as a parent
    if n_elements(*self.parent_ids) ne 0 then begin
        primaries = where(*self.parent_ids eq self.rootID, count)
        if count ne 0 then begin
            ;destroy them
            for i = 0, n_elements(primaries)-1 $
                do widget_control, (*self.tree_ids)[primaries[i]], /destroy
        endif
    endif

;---------------------------------------------------------------------
;Create the New Tree Structure ///////////////////////////////////////
;---------------------------------------------------------------------
    nShown = 0
    vnames = self.oCDF -> get_varname()
    
    ;Total number of variables
    self.oCDF -> GetProperty, NVARS=nvars
    
    ;Loop through the variables
    for i = 0, nvars - 1 do begin
    ;---------------------------------------------------------------------
    ;Skip Non-Data Variables? ////////////////////////////////////////////
    ;---------------------------------------------------------------------
        self.oCDF -> get_ConfigVals, vnames[i], VAR_TYPE=var_type
        if n_elements(var_type) eq 0 then begin
            var_type = ''
            self.show_all = 1
        endif
        
        if self.show_all eq 0 and strupcase(var_type) ne 'DATA' then continue
        
        ;Get the names of the labels and dependent variables.
        self.oCDF -> get_ConfigVals, vnames[i], DEPEND_1=depend_1, DEPEND_2=depend_2, $
                                                DEPEND_3=depend_3

        ;Components or Depend_#
        self -> Create_Node, vnames[i], depend_1, depend_2, depend_3
        nShown += 1
    endfor

;---------------------------------------------------------------------
;Set the Value of the Tree Label /////////////////////////////////////
;---------------------------------------------------------------------
    ;Create values for the label and button
    if self.show_all then begin
        label_string = string(FORMAT='(%"%i Data Variables (Shown), ' + $
                                        '%i Other Variables (Not Shown)")', $
                                        nShown, nvars - nShown)
        button_string = 'Fewer'
    endif else begin
        label_string = string(FORMAT='(%"All %i Variables Listed.")', nShown)
        button_string = 'Show All'
    endelse
    
    ;Set the label value
    treeLabel = widget_info(event.top, FIND_BY_UNAME='treeLabel')
    widget_control, treeLabel, SET_VALUE=label_string
    
    ;Set the button value
    showAll = widget_info(event.top, FIND_BY_UNAME='showAll')
    widget_control, showAll, SET_VALUE=button_string
end


;+
;   The purpose of this method is to handle events triggered by the List widgets.
;
; :Params:
;       EVENT:                  in, required, type=structure
;                               A button_event structure generated windows manager.
;-
pro CDF_VarSelect::List_Events, event
    ;do nothing
end


;+
;   The purpose of this program is to handle events generated by the OK button.
;   Specifically, it determines which data labels from the list pane, if any, were 
;   selected.
;
; :Params:
;       EVENT:                  in, required, type=structure
;                               A button_event structure generated by a button press
;-
pro CDF_VarSelect::Ok, event
    compile_opt idl2, hidden
    
    ;catch errors
    catch, the_error
    if the_error ne 0 then begin
        catch, /cancel
        void = cgErrorMsg()
        return
    endif

;---------------------------------------------------------------------
;Which List Elements were Selected? //////////////////////////////////
;---------------------------------------------------------------------
    
    ;Step through each list
    for i = 0, 2 do begin
        list_id = widget_info(event.top, FIND_BY_UNAME='LIST_' + string(i, FORMAT='(i1)'))
        iSelect = widget_info(list_id, /LIST_SELECT)

        ;If no items were selected, assume user wants all of them
        if n_elements(iSelect) eq 1 && iSelect eq -1 then begin
            
            ;Select all indices.
            case i of
                0: if n_elements(*self.label_1) gt 0 $
                    then *self.label_1 = lindgen(n_elements(*self.label_1))
                1: if n_elements(*self.label_2) gt 0 $
                    then *self.label_2 = lindgen(n_elements(*self.label_2))
                2: if n_elements(*self.label_3) gt 0 $
                    then *self.label_3 = lindgen(n_elements(*self.label_3))
            endcase

        endif else begin
        
            ;Store selected indices.
            case i of
                0: *self.label_1 = iSelect
                1: *self.label_2 = iSelect
                2: *self.label_3 = iSelect
            endcase
            
        endelse
    endfor

;---------------------------------------------------------------------
;Clean Up ////////////////////////////////////////////////////////////
;---------------------------------------------------------------------
    
    ;Destroy the GUI
    self -> Quit, event
end


;+
;   The purpose of this method is to retrieve object properties.
;
; :Keywords:
;-
pro CDF_VarSelect::GetProperty, $
LABEL_1 = label_1, $
LABEL_2 = label_2, $
LABEL_3 = label_3, $
DEPEND = depend, $
DIMENSION = dimension, $
VARIABLE = variable
    compile_opt idl2

    if arg_present(label_1) and n_elements(*self.label_1) ne 0 then label_1 = *self.label_1
    if arg_present(label_2) and n_elements(*self.label_2) ne 0 then label_2 = *self.label_2
    if arg_present(label_3) and n_elements(*self.label_3) ne 0 then label_3 = *self.label_3
    if arg_present(depend) then depend = self.thisDepend
    if arg_present(dimension) then dimension = self.thisDim
    if arg_present(variable) then variable = self.thisVar
end



;+
;   The purpose of this program is to perform some cleanup and destroy the widget. It
;   is called by, e.g., Ok.
;
; :Params:
;       EVENT:              in, required, type=structure
;                           An event structure returned by the windows manager.
;-
pro CDF_VarSelect::Quit, event
    compile_opt idl2

    ;If the cancel button was pressed, then set the cancel flag.
    if n_elements(event) ne 0 then begin
        uname = widget_info(event.id, /UNAME)
        if strupcase(uname) eq 'CANCEL' then self.cancel = 1
    endif
    
    ;Make sure the labels are not still stored
    if size(*self.label_1, /TYPE) eq 7 then begin
        ptr_free, self.label_1
        self.label_1 = ptr_new(/ALLOCATE_HEAP)
    endif
    
    if size(*self.label_2, /TYPE) eq 7 then begin
        ptr_free, self.label_2
        self.label_2 = ptr_new(/ALLOCATE_HEAP)
    endif
    
    if size(*self.label_3, /TYPE) eq 7 then begin
        ptr_free, self.label_3
        self.label_3 = ptr_new(/ALLOCATE_HEAP)
    endif
    
    ;Destroy the pointers
    ptr_free, self.tree_ids
    ptr_free, self.parent_ids
    ptr_free, self.unames
    
    ;Destroy the widget.
    widget_control, event.top, /destroy
end


;+
;   The purpose of this method is to create a GUI from which a CDF Variable can be
;   selected.
;
; :Params:
;       GROUP_LEADER:       in, optional, type=int
;                           The widget ID of an existing widget that serves as group
;                               leader for the newly-created widget.
;-
pro CDF_VarSelect::Realize_GUI, group_leader
	compile_opt idl2
    
    ;Catch errors
    catch, the_error
    if the_error ne 0 then begin
        catch, /cancel
        void = cgErrorMsg()
        return
    endif

;---------------------------------------------------------------------
;Make the Top Level Base /////////////////////////////////////////////
;---------------------------------------------------------------------

	;Make a top-level base with or without a groupleader. cdf_read_gui2 is called by other
	;blocking widgets, so if a group_leader is given, then make cdf_read_gui2 modal.
	if n_elements(group_leader) ne 0 then begin
	    no_block = 0
	    self.tlb = widget_base(GROUP_LEADER=group_leader, TITLE='Read CDF', /COLUMN, $
                               XOFFSET=100, YOFFSET=100, UNAME='tlb', /BASE_ALIGN_CENTER, $
                               /MODAL)
	endif else begin
	    no_block = 0
	    self.tlb = widget_base(TITLE='Read CDF', /COLUMN, XOFFSET=200, YOFFSET=100, $
                               UNAME='tlb', /BASE_ALIGN_CENTER)
	endelse
    
;---------------------------------------------------------------------
;Create Tree Root ////////////////////////////////////////////////////
;---------------------------------------------------------------------
    ;Put a label above the tree saying how many data variables are shown and how
    ;many other variables are shown/hidden.
    treeLabel = widget_label(self.tlb, VALUE='', /ALIGN_LEFT, FRAME=2, UNAME='treeLabel')
    infoBase = widget_base(self.tlb, ROW=1)
    
    ;Adjust the width of the widget to fit the width of the longest variable name
    vnames = self.oCDF -> get_varname()
    varwidth = max(strlen(vnames) + 10) * !d.x_ch_size
    
    ;Create the root of the widget tree.
    treeBase = widget_base(infoBase, COL=1, /ALIGN_LEFT)
    self.rootID = widget_tree(treeBase, /FOLDER, /EXPANDED, $
                              VALUE='filename', UNAME='treeRoot', XSIZE=varwidth, YSIZE=20*!d.y_ch_size)

;---------------------------------------------------------------------
;Create Informational Text Box ///////////////////////////////////////
;---------------------------------------------------------------------

    ;Put it next to the Tree Widget
    infoText = widget_text(infoBase, VALUE='', /WRAP, XSIZE=50, YSIZE=15, /SCROLL, $
                           UNAME='infotext')

;---------------------------------------------------------------------
;Create Widget Lists /////////////////////////////////////////////////
;---------------------------------------------------------------------

    listBase = widget_base(self.tlb, COL=3)
    banner = widget_label(listBase, /ALIGN_CENTER, VALUE=' ', UNAME='BANNER_0', XSIZE=!D.x_ch_size*19)
    list = widget_list(listBase, /MULTIPLE, VALUE='', UNAME='LIST_0', XSIZE=20, YSIZE=10, $
                       UVALUE={object: self, method:'List_Events'})
    
    banner = widget_label(listBase, /ALIGN_CENTER, VALUE=' ', UNAME='BANNER_1', XSIZE=!D.x_ch_size*19)
    list = widget_list(listBase, /MULTIPLE, VALUE='', UNAME='LIST_1', XSIZE=20, YSIZE=10, $
                       UVALUE={object: self, method:'List_Events'})
    
    banner = widget_label(listBase, /ALIGN_CENTER, VALUE=' ', UNAME='BANNER_2', XSIZE=!D.x_ch_size*19)
    list = widget_list(listBase, /MULTIPLE, VALUE='', UNAME='LIST_2', XSIZE=20, YSIZE=10, $
                       UVALUE={object: self, method:'List_Events'})

;---------------------------------------------------------------------
;Create OK and Cancel and "Show All" Buttons /////////////////////////
;---------------------------------------------------------------------
    okBase = widget_base(self.tlb, ROW=1)
    button = widget_button(okBase, /ALIGN_CENTER, UNAME='showAll', VALUE='Show All', $
                           UVALUE={object: self, method:'Create_Tree'})
    button = widget_button(okBase, /ALIGN_CENTER, UNAME='ok', VALUE='OK', $
                           UVALUE={object: self, method:'Ok'})
    button = widget_button(okBase, /ALIGN_CENTER, UNAME='cancel', VALUE='Cancel', $
                           UVALUE={object: self, method:'Quit'})

;---------------------------------------------------------------------
;Validate Pointers and Build the Tree ////////////////////////////////
;---------------------------------------------------------------------
    
    ;Validate GUI Pointers
    self.tree_ids = ptr_new(/ALLOCATE_HEAP)
    self.parent_ids = ptr_new(/ALLOCATE_HEAP)
    self.unames = ptr_new(/ALLOCATE_HEAP)
    self.label_1 = ptr_new(/ALLOCATE_HEAP)
    self.label_2 = ptr_new(/ALLOCATE_HEAP)
    self.label_3 = ptr_new(/ALLOCATE_HEAP)
    
    ;Set SHOW_ALL to 1 so that it will be immediately toggled to 0 in Create_Tree
    self.show_all = 1
    
    ;Simulate an event to build the tree.
    self -> Create_Tree, {widget_button, $
                          ID: self.tlb, $
                          TOP: self.tlb, $
                          HANDLER: 0L, $
                          SELECT: 1}
    
;---------------------------------------------------------------------
;Create the State Variable, Realize, and Start Event Handling ////////
;---------------------------------------------------------------------

	;Realize the top-level base
	widget_control, self.tlb, /REALIZE
	
	;Call XMANAGER
	xmanager, 'CDF_VarSelect', self.tlb, cleanup='CDF_VarSelect_Cleanup', $
	          event_handler='CDF_VarSelect_Events', NO_BLOCK=no_block

end


;+
;   The purpose of this method is to handle events triggered by the widget tree.
;
;   Current tasks::
;
;       1. Check if the selection has changed.
;       2. Update the information in the text box
;       3. Update the widget labels (called "banners" here to distinguish them from CDF
;           data labels) describing what is contained in the list panes
;       4. Update the data labels in the list panes
;
;   Note that:
;
;       CDF files are often disorganized and the data labels do not match the corresponding
;       DEPEND_# variable (e.g. DEPEND_1 does not correspond to LABL_PTR_1, nor does it
;       correspond to the first dimension of VARIABLE). Much of what follows is intended
;       to organize them. This will pay off later, when the "ok" button is pressed.
;
;
; :Params:
;       EVENT:                  in, required, type=structure
;                               A button_event structure generated the windows manager
;-
pro CDF_VarSelect::Tree_Events, event
    compile_opt idl2
    
    ;catch errors
    catch, the_error
    if the_error ne 0 then begin
        catch, /cancel
        void = cgErrorMsg()
        return
    endif

    ;RETURN -- if this was not a select event
    ;       -- if a folder was selected. Updating the labels causes the widget to be
    ;           repositioned for some reason.
    if event.type ne 0 then return

;---------------------------------------------------------------------
;Has the Selection Changed? //////////////////////////////////////////
;---------------------------------------------------------------------
    
    ;Find the parent ID
    thisID = event.id
    parentID = widget_info(event.id, /PARENT)
    
    ;Keep going until the parent is the root ID.
    while parentID ne self.rootID do begin
        thisID = parentID
        parentID = widget_info(thisID, /PARENT)
    endwhile
    
    ;RETURN -- If the proper lists are already displayed
    if self.displayed_list_id eq thisID then return

;---------------------------------------------------------------------
;Get the Leaf and Variable Name //////////////////////////////////////
;---------------------------------------------------------------------
    
    ;Store the UName of the selected leaf/branch. DEPEND_# UNames are preceded
    ;by "DEPEND_[123]: ", which can be used to determine which dimension of the
    ;variable is to be displayed.
    uname = widget_info(event.id, /UNAME)
    self.thisDepend = uname

    ;Get the variable name associated with the selected folder or leaf
    ivar = where(*self.tree_ids eq thisID)
    vname = ((*self.unames)[ivar])[0]           ;make sure it is scalar
    self.thisVar = vname

;---------------------------------------------------------------------
;Update Text Widget //////////////////////////////////////////////////
;---------------------------------------------------------------------
    self.displayed_text_id = thisID
    self -> Update_infoBox, event, vname

;---------------------------------------------------------------------
;Fill the Lists //////////////////////////////////////////////////////
;---------------------------------------------------------------------
    self -> Update_Banners, event, vname
    self -> Update_Lists, event, vname
end


;+
;   The purpose of this method is to update the banners that describe the list panes
;   when a new leaf is selected.
;
; :Params:
;       EVENT:          in, required, type=structure
;                       A button_event structure generated by the windows manager.
;       VNAME:          in, required, type=string
;                       The variable name whose data labels are to be displayed in the
;                           list panes of the GUI.
;-
pro CDF_VarSelect::Update_Banners, event, vname
    compile_opt idl2
    
    ;catch errors
    catch, the_error
    if the_error ne 0 then begin
        catch, /cancel
        void = cgErrorMsg()
        return
    endif

;---------------------------------------------------------------------
;Fill the Labels /////////////////////////////////////////////////////
;---------------------------------------------------------------------
    
    ;Depend_N
    self.oCDF -> get_ConfigVals, vname, $
                                 DEPEND_1=depend_1, $
                                 DEPEND_2=depend_2, $
                                 DEPEND_3=depend_3
    if n_elements(depend_1) eq 0 then depend_1 = ''
    if n_elements(depend_2) eq 0 then depend_2 = ''
    if n_elements(depend_3) eq 0 then depend_3 = ''
    depend_n = [depend_1, depend_2, depend_3]

    ;Label IDs
    label_ids = intarr(3)
    label_ids[0] = widget_info(event.top, FIND_BY_UNAME='BANNER_0')
    label_ids[1] = widget_info(event.top, FIND_BY_UNAME='BANNER_1')
    label_ids[2] = widget_info(event.top, FIND_BY_UNAME='BANNER_2')

    ;Step through DEPEND_[1-3]
    for iDep = 0, 2 do begin

        ;If DEPEND_# exists, then update the label.
        if depend_n[iDep] ne '' $
            then widget_control, label_ids[iDep], SET_VALUE=depend_n[iDep] $
            else widget_control, label_ids[iDep], SET_VALUE=' '
    endfor
end


;+
;   The purpose of this method is to update the text box to diplay information relating
;   to the selected variable (or the variable associated with the selected DEPEND_#
;   variable).
;
; :Params:
;       EVENT:          in, required, type=structure
;                       A button_event structure generated the windows manager.
;       VNAME:          in, required, type=string
;                       The variable name whose data labels are to be displayed in the
;                           list panes of the GUI.
;-
pro CDF_VarSelect::Update_infoBox, event, vname
    compile_opt idl2
    
    ;catch errors
    catch, the_error
    if the_error ne 0 then begin
        catch, /cancel
        void = cgErrorMsg()
        return
    endif

    ;Newline character
    if (!D.NAME eq 'WIN') $
        then newline = string([13B, 10B]) $
        else newline = string(10B)

    ;Get variable properties
    self.oCDF -> get_varinfo, vname, MAXREC=maxrec, DIM=dim
    text_string = vname
    text_string += newline + '    Maxrec = ' + strtrim(maxrec, 2)
    text_string += newline + '    Dim    = [' + strjoin(strtrim(dim, 2), ', ') + ']'
    text_string += newline + newline
    
    ;Check if the dependent variables exist within the file
    self.oCDF -> Get_ConfigVals, vname, $
                                 DEPEND_0=depend_0, DEPEND_1=depend_1, $
                                 DEPEND_2=depend_2, DEPEND_3=depend_3

    if n_elements(depend_0) eq 0 then depend_0 = ''
    if n_elements(depend_1) eq 0 then depend_1 = ''
    if n_elements(depend_2) eq 0 then depend_2 = ''
    if n_elements(depend_3) eq 0 then depend_3 = ''

    depend_arr = [depend_0, depend_1, depend_2, depend_3]
    tf_has = self.oCDF -> AttExists(depend_arr)
    
    ;Get information about each dependent variable
    text_string += 'DEPEND' + newline
    for i = 0, n_elements(tf_has) - 1 do begin
        ;If the file does not have the DEPEND_# attribute, then continue
        if tf_has[i] eq 0 then continue
        depend_name = self.oCDF -> get_varatt(vname, depend_arr[i])
        
        ;If the variable does not have the !DEPEND_# attribute, then continue
        if depend_name eq !Null then continue
        
        self.oCDF -> get_varinfo, depend_name, MAXREC=maxrec, DIM=dim
        
        text_string += '    ' + string(i, format='(i0)') + ': '
        text_string += depend_name + '(Maxrec = ' + strtrim(maxrec, 2) + ', ' + $
                                     'Dim = [' + strjoin(strtrim(dim, 2), ', ') + '])'
        text_string += newline
    endfor
    text_string += newline

    ;Get the description and notes pertaining to the variable
    self.oCDF -> get_ConfigVals, vname, CATDESC=catdesc, VAR_NOTES=var_notes
    if n_elements(catdesc) ne 0 then text_string += catdesc
    text_string += newline + newline
    if n_elements(var_notes) ne 0 then text_string += var_notes
    
    ;Update the text widget
    textID = widget_info(event.top, FIND_BY_UNAME='infotext')
    widget_control, textID, SET_VALUE=text_string
end


;+
;   The purpose of this method is to update the list panes when a new leaf is selected.
;
; :Params:
;       EVENT:          in, required, type=structure
;                       A button_event structure generated the windows manager
;       VNAME:          in, required, type=string
;                       The variable name whose data labels are to be displayed in the
;                           list panes of the GUI.
;-
pro CDF_VarSelect::Update_Lists, event, vname
    compile_opt idl2
    
    ;catch errors
    catch, the_error
    if the_error ne 0 then begin
        catch, /cancel
        void = cgErrorMsg()
        return
    endif

;---------------------------------------------------------------------
;Fill the Lists //////////////////////////////////////////////////////
;---------------------------------------------------------------------

    ;Make the label pointers easier to get to
    self.oCDF -> Get_ConfigVals, vname, $
                                 LABL_PTR_1=labl_ptr_1, $
                                 LABL_PTR_2=labl_ptr_2, $
                                 LABL_PTR_3=labl_ptr_3
                                 
    if n_elements(labl_ptr_1) eq 0 then labl_ptr_1 = ''
    if n_elements(labl_ptr_2) eq 0 then labl_ptr_2 = ''
    if n_elements(labl_ptr_3) eq 0 then labl_ptr_3 = ''
    label_pointers = [labl_ptr_1, labl_ptr_2, labl_ptr_3]
    
    ;Depend_N
    self.oCDF -> get_ConfigVals, vname, $
                                 DEPEND_1=depend_1, $
                                 DEPEND_2=depend_2, $
                                 DEPEND_3=depend_3
    if n_elements(depend_1) eq 0 then depend_1 = ''
    if n_elements(depend_2) eq 0 then depend_2 = ''
    if n_elements(depend_3) eq 0 then depend_3 = ''
    depend_n = [depend_1, depend_2, depend_3]
    
    ;IDs of the widget lists
    list_ids = indgen(3)
    list_ids[0] = widget_info(event.top, FIND_BY_UNAME='LIST_0')
    list_ids[1] = widget_info(event.top, FIND_BY_UNAME='LIST_1')
    list_ids[2] = widget_info(event.top, FIND_BY_UNAME='LIST_2')
    
    ;Step through all of the labels
    for iLabel = 0, 2 do begin

        ;If LABEL_# exits, fill the list with data labels.
        if self.oCDF -> varHasAtt(vname, label_pointers[iLabel], ATT_VALUE=label_name) then begin
            temp_labels = self.oCDF -> Read(label_name, /STRING)
            widget_control, list_ids[iLabel], SET_VALUE=temp_labels
        
        ;If DEPEND_N exists, use the data values as labels.
        endif else if depend_n[iLabel] ne '' then begin
            label_values = self.oCDF -> Read(depend_n[iLabel])
            temp_labels = string(label_values, FORMAT='(F0.3)')
            widget_control, list_ids[iLabel], SET_VALUE=temp_labels
        
        ;Otherwise, clear the list
        endif else begin
            widget_control, list_ids[iLabel], SET_VALUE=''
        endelse
    endfor
end


;+
;   The purpose of this method is to foward events to the proper event-handling method.
;
; :Params:
;       EVENT:              in, required, type=structure
;                           An event structure returned by the windows manager.
;-
pro CDF_VarSelect_Events, event
    compile_opt idl2
    
    ;catch errors
    catch, the_error
    if the_error ne 0 then begin
        catch, /cancel
        void = cgErrorMsg()
        return
    endif
    
    ;Get the user value and call the appropriate event handling method.
    widget_control, event.id, GET_UVALUE=event_handler

    ;Handle Scrool Wheel events differently
    call_method, event_handler.method, event_handler.object, event

end


;+
;   The purpose of this program is to clean up after the widget is destroyed. CDF_VarSelect
;   object itself will not be destroyed.
;-
pro CDF_VarSelect_Cleanup, tlb
    ;GUI pointers will be destroyed when the CDF_VarSelect object is destroyed
end


;+
;   The purpose of this method is to destroy the object.
;
; :Params:
;       EVENT:          in, required, type=structure
;                       A event structure generated the windows manager.
;-
pro CDF_VarSelect::Destroy, event
    obj_destroy, self
end


;+
;   The purpose of this method is to clean up after the object is destroyed. If a CDF_VarSelect
;   GUI is open, it will also be destroyed.
;-
pro CDF_VarSelect::Cleanup
	compile_opt idl2
    
    ;Catch errors
    catch, the_error
    if the_error ne 0 then begin
        catch, /cancel
        void = cgErrorMsg()
        return
    endif

    ;Destroy the widget, if it exists
    isValid = widget_info(self.tlb, /VALID_ID)
    if isValid then widget_control, self.tlb, /DESTROY

    ;Free up pointers associated with the gui 
    ptr_free, self.label_1
    ptr_free, self.label_2
    ptr_free, self.label_3
    ptr_free, self.parent_ids
    ptr_free, self.tree_ids
    ptr_free, self.unames
end


;+
;   The purpose of this method is to initialize the CDF_Read object reference.
;
; :Params:
;       thisCDF:        in, optional, type=string/object
;                       Either the file name of the CDF data file from which data is to
;                           be opened and plotted, or a valid CDF_Read object reference.
;                           If not provided, a file selection dialog box will appear.
;
; :Keywords:
;       GROUP_LEADER:   in, optional, type=integer
;                       The group leader of the file selection gui. Use only when
;                           `FILENAME` undefined.
;       _REF_EXTRA:     in, optional, type=any
;                       All keyword accepted by the CDF_READ::SET_CONFIG method are also
;                           accepted for keyword inheritance.
;
; :Returns:
;       object reference
;-
function CDF_VarSelect::Init, thisCDF, $
GROUP_LEADER = group_leader, $
_REF_EXTRA = extra
	compile_opt idl2
    
    ;Catch errors
    catch, the_error
    if the_error ne 0 then begin
        catch, /cancel
        void = cgErrorMsg()
        return, 0
    endif
    
    ;Allocate Pointers
    label_1 = ptr_new(/ALLOCATE_HEAP)
    label_2 = ptr_new(/ALLOCATE_HEAP)
    label_3 = ptr_new(/ALLOCATE_HEAP)
    parent_ids = ptr_new(/ALLOCATE_HEAP)
    tree_ids = ptr_new(/ALLOCATE_HEAP)
    unames = ptr_new(/ALLOCATE_HEAP)
	
	szCDF = size(thisCDF, /TYPE)
        
;---------------------------------------------------------------------
;OBJECT REFERENCE ////////////////////////////////////////////////////
;---------------------------------------------------------------------
	if szCDF eq 11 then begin
	    ;Is the object valid?
	    if obj_valid(thisCDF) eq 0 $
	        then message, 'thisCDF is not a valid object reference.' $
	        
	        ;Is it a CDF_Read object?
	        else if isa(thisCDF, 'CDF_Read') $
	            then self.oCDF = thisCDF $
	            else message, 'thisCDF must be a CDF_Read object.'
        
;---------------------------------------------------------------------
;FILENAME ////////////////////////////////////////////////////////////
;---------------------------------------------------------------------
	endif else if szCDF eq 7 then begin
	    oCDF = CDF_Read(thisCDF)
	    if obj_valid(oCDF) $
	        then self.oCDF = oCDF $
	        else return, 0
        
;---------------------------------------------------------------------
;SELECT FILE /////////////////////////////////////////////////////////
;---------------------------------------------------------------------
	endif else begin
	
	    oCDF = CDF_Read(_EXTRA=extra)
	    if obj_valid(oCDF) $
	        then self.oCDF = oCDF $
	        else return, 0 
	endelse
	
	;Realize the GUI
	self -> Realize_GUI, group_leader
	
	;Was the cancel button pressed?
	if self.cancel eq 1 $
	    then return, 0 $
	    else return, 1
end


;+
;   Define the CDF_VarSelect class
;
; :Params:
;       CLASS:          out, optional, type=structure
;                       The class definition structure.
;
; :Fields:
;       TLB:                Top level base of the file- and variable-selection widgets
;       OCDF:               An object reference to a CDF_Read object
;       DISPLAYED_LIST_ID:  Widget ID of the wTree leaf displayed in the wLists
;       DISPLAYED_TEXT_ID:  Widget ID of the wTree leaf displayed in the wText
;       LABEL_1:            Indices of a subarray of DEPEND_1
;       LABEL_2:            Indices of a subarray of DEPEND_2
;       LABEL_3:            Indices of a subarray of DEPEND_3
;       PARENT_IDS:         Widget IDs of the tree_ids' parents
;       ROOTID:             Widget ID of the wTree root
;       SHOW_ALL:           Flag for showing only the data variables
;       THISDEPEND:         Dependent variable associated with the chosen leaf
;       THISDIM:            Dimension to be displayed
;       THISVAR:            Variable associated with the chosen leaf
;       TREE_IDS:           Widget ID of all of the wTree nodes
;       UNAMES:             User name mapping to tree_ids and parent_ids.
;-
pro CDF_VarSelect__define, class
	compile_opt idl2
	
	class = { CDF_VarSelect, $
	          tlb: 0, $                     ;Top Level Base
	          oCDF: obj_new(), $            ;An object reference to a CDF_Read object
	          cancel: 0, $                  ;Cancel flag
	          displayed_list_id: 0, $       ;Widget ID of the wTree leaf displayed in the wLists
	          displayed_text_id: 0, $       ;Widget ID of the wTree leaf displayed in the wText
	          label_1: ptr_new(), $         ;Indices of a subarray of DEPEND_1
	          label_2: ptr_new(), $         ;Indices of a subarray of DEPEND_2
	          label_3: ptr_new(), $         ;Indices of a subarray of DEPEND_3   
	          parent_ids: ptr_new(), $      ;wIDs of the tree_ids' parents
	          rootID: 0, $                  ;wID of the wTree root
	          show_all: 0, $                ;Flag for showing only the data variables
	          thisDepend: '', $             ;Dependent variable associated with the chosen leaf
	          thisDim: 0, $                 ;Dimension to be displayed
	          thisVar: '', $                ;Variable associated with the chosen leaf
	          tree_ids: ptr_new(), $        ;wID of all of the wTree nodes
	          unames: ptr_new() $           ;User name mapping to tree_ids and parent_ids.
	        }
end